<!DOCTYPE HTML>
<html lang="zh-CN">

<head>
    <!--Setting-->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta http-equiv="Cache-Control" content="no-siteapp">
    <meta http-equiv="Cache-Control" content="no-transform">
    <meta name="renderer" content="webkit|ie-comp|ie-stand">
    <meta name="apple-mobile-web-app-capable" content="涛哥的博客">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="format-detection" content="telephone=no,email=no,adress=no">
    <meta name="browsermode" content="application">
    <meta name="screen-orientation" content="portrait">
    <meta name="theme-version" content="1.2.3">
    <meta name="root" content="/">
    <link rel="dns-prefetch" href="https://jaredtao.github.io">
    <!--SEO-->

<meta name="keywords" content="Qt,Qt实用技能,信号-槽" />


<meta name="description" content="
简介
信号与槽的声明
信号-槽的使用
信号的使用
槽函数的使用
信号-槽的”元调用”


信号和信号的参数
注册元类型


信号-槽的连接 connect函数
连接的不同写法
元方法式
函数指..." />


<meta name="robots" content="all" />
<meta name="google" content="all" />
<meta name="googlebot" content="all" />
<meta name="verify" content="all" />
    <!--Title-->

<title>
    
    玩转Qt(8)-掌握信号槽使用细节 |
    
    涛哥的博客
</title>

<link rel="alternate" href="/atom.xml" title="涛哥的博客" type="application/atom+xml">


<link rel="icon" href="/favicon.ico">

    


<link rel="stylesheet" href="/css/bootstrap.min.css?rev=3.3.7.css">
<link rel="stylesheet" href="/css/font-awesome.min.css?rev=4.7.0.css">
<link rel="stylesheet" href="/css/style.css?rev=@@hash.css">

    
<div class="hide">
    <script type="text/javascript">
    var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");
    document.write(unescape("%3Cspan class='cnzz_stat_icon_1263868967 hide' %3E%3Cscript%20src%3D%22https%3A%2F%2Fs95.cnzz.com%2Fz_stat.php%3Fweb_id%3D1272564536%22%3E%3C%2Fscript%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s19.cnzz.com/z_stat.php%3Fid%3D1263868967%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));
    </script>
</div>




    

<script>
(function() {
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
    } else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>

<meta name="generator" content="Hexo 4.2.1"></head>
<!--[if lte IE 8]>
<style>
    html{ font-size: 1em }
</style>
<![endif]-->
<!--[if lte IE 9]>
<div style="ie">你使用的浏览器版本过低，为了你更好的阅读体验，请更新浏览器的版本或者使用其他现代浏览器，比如Chrome、Firefox、Safari等。</div>
<![endif]-->
<body>
    <header class="main-header"  style="background-image:url(
    http://snippet.shenliyang.com/img/banner.jpg)"
     >
    <div class="main-header-box">
        <a class="header-avatar" href="/" title='JaredTao'>
            <img src="/img/avatar.jpg" alt="logo头像" class="img-responsive center-block">
        </a>
        <div class="branding">
            <!--<h2 class="text-hide">Snippet主题,从未如此简单有趣</h2>-->
            
            <h2>
                单枪匹马亦对饮，历经磨难记初心
            </h2>
            
        </div>
    </div>
</header>
    <nav class="main-navigation">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="navbar-header"><span class="nav-toggle-button collapsed pull-right" data-toggle="collapse" data-target="#main-menu" id="mnav">
                        <span class="sr-only"></span>
                        <i class="fa fa-bars"></i>
                    </span>
                    <a class="navbar-brand" href="https://jaredtao.github.io">
                        涛哥的博客</a>
                </div>
                <div class="collapse navbar-collapse" id="main-menu">
                    <ul class="menu">
                        
                        <li role="presentation" class="text-center">
                            <a href="/"><i class="fa "></i>
                                首页</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/玩转Qt/"><i class="fa "></i>
                                玩转Qt</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/玩转Qml/"><i class="fa "></i>
                                玩转Qml</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/玩转QtQuick/"><i class="fa "></i>
                                玩转QtQuick</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/涛哥的博客/"><i class="fa "></i>
                                涛哥的博客</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/archives/"><i class="fa "></i>
                                时间轴</a>
                        </li>
                        
                    </ul>
                </div>
            </div>
        </div>
    </div>
</nav>
    <section class="content-wrap">
        <div class="container">
            <div class="row">
                <main class="col-md-8 main-content m-post">
                    <p id="process"></p>
<article class="post">
    <div class="post-head">
        <h1 id="玩转Qt(8)-掌握信号槽使用细节">
            
            玩转Qt(8)-掌握信号槽使用细节
            
        </h1>
        <div class="post-meta">
    
    <span class="categories-meta fa-wrap">
        <i class="fa fa-folder-open-o"></i>
        <a class="category-link" href="/categories/%E7%8E%A9%E8%BD%ACQt/">玩转Qt</a>
    </span>
    
    
    <span class="fa-wrap">
        <i class="fa fa-tags"></i>
        <span class="tags-meta">
            
            <a class="tag-link" href="/tags/Qt/" rel="tag">Qt</a> <a class="tag-link" href="/tags/Qt%E5%AE%9E%E7%94%A8%E6%8A%80%E8%83%BD/" rel="tag">Qt实用技能</a> <a class="tag-link" href="/tags/%E4%BF%A1%E5%8F%B7-%E6%A7%BD/" rel="tag">信号-槽</a>
            
        </span>
    </span>
    
    
    
    <span class="fa-wrap">
        <i class="fa fa-clock-o"></i>
        <span class="date-meta">
            2019/09/02</span>
    </span>
    
    <span class="fa-wrap">
        <i class="fa fa-eye"></i>
        <span id="busuanzi_value_page_pv"></span>
    </span>
    
    
</div>
        
        
        <p class="fa fa-exclamation-triangle warning">
            本文于<strong>
                1925</strong>
            天之前发表，文中内容可能已经过时。
        </p>
        
    </div>
    
<div id="post-gallery">
    
    <img src="/images/Qt4/signals-slot.png" alt="gallery-img" class="gallery">
    
</div>

    <div class="post-body post-content">
        <ul>
<li><a href="#%e7%ae%80%e4%bb%8b">简介</a></li>
<li><a href="#%e4%bf%a1%e5%8f%b7%e4%b8%8e%e6%a7%bd%e7%9a%84%e5%a3%b0%e6%98%8e">信号与槽的声明</a></li>
<li><a href="#%e4%bf%a1%e5%8f%b7-%e6%a7%bd%e7%9a%84%e4%bd%bf%e7%94%a8">信号-槽的使用</a><ul>
<li><a href="#%e4%bf%a1%e5%8f%b7%e7%9a%84%e4%bd%bf%e7%94%a8">信号的使用</a></li>
<li><a href="#%e6%a7%bd%e5%87%bd%e6%95%b0%e7%9a%84%e4%bd%bf%e7%94%a8">槽函数的使用</a></li>
<li><a href="#%e4%bf%a1%e5%8f%b7-%e6%a7%bd%e7%9a%84%22%e5%85%83%e8%b0%83%e7%94%a8%22">信号-槽的”元调用”</a></li>
</ul>
</li>
<li><a href="#%e4%bf%a1%e5%8f%b7%e5%92%8c%e4%bf%a1%e5%8f%b7%e7%9a%84%e5%8f%82%e6%95%b0">信号和信号的参数</a><ul>
<li><a href="#%e6%b3%a8%e5%86%8c%e5%85%83%e7%b1%bb%e5%9e%8b">注册元类型</a></li>
</ul>
</li>
<li><a href="#%e4%bf%a1%e5%8f%b7-%e6%a7%bd%e7%9a%84%e8%bf%9e%e6%8e%a5-connect%e5%87%bd%e6%95%b0">信号-槽的连接 connect函数</a><ul>
<li><a href="#%e8%bf%9e%e6%8e%a5%e7%9a%84%e4%b8%8d%e5%90%8c%e5%86%99%e6%b3%95">连接的不同写法</a><ul>
<li><a href="#%e5%85%83%e6%96%b9%e6%b3%95%e5%bc%8f">元方法式</a></li>
<li><a href="#%e5%87%bd%e6%95%b0%e6%8c%87%e9%92%88%e5%bc%8f">函数指针式</a><ul>
<li><a href="#%e5%87%bd%e6%95%b0%e9%87%8d%e8%bd%bd%e7%9a%84%e5%a4%84%e7%90%86">函数重载的处理</a></li>
</ul>
</li>
<li><a href="#functor%e5%bc%8f">functor式</a><ul>
<li><a href="#%e5%85%b3%e4%ba%8efunctor">关于functor</a></li>
<li><a href="#functor">functor</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#connect%e7%9a%84%e8%bf%9e%e6%8e%a5%e7%b1%bb%e5%9e%8b">connect的连接类型</a></li>
<li><a href="#connect%e7%9a%84%e8%bf%94%e5%9b%9e%e5%80%bc">connect的返回值</a></li>
</ul>
</li>
</ul>
<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>之前的文章《认清信号槽的本质》、《窥探信号槽的实现细节》讨论了一些原理，</p>
<p>这次我们来讨论一些信号-槽的使用细节。</p>
<h1 id="信号与槽的声明"><a href="#信号与槽的声明" class="headerlink" title="信号与槽的声明"></a>信号与槽的声明</h1><p>要使用信号-槽功能，先决条件是继承QObject类，并在类声明中增加Q_OBJECT宏。</p>
<p>之后在”signals:” 字段之后声明一些函数，这些函数就是信号。</p>
<p>在”public slots:” 之后声明的函数，就是槽函数。</p>
<p>例如下面的代码:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Tom.h</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> once</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;QObject&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;QDebug&gt;</span></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Tom</span> :</span> <span class="keyword">public</span> QObject</span><br><span class="line">&#123;</span><br><span class="line">    Q_OBJECT</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    Tom(QObject *parent = <span class="literal">nullptr</span>) : QObject(parent)</span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">miaow</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        qDebug() &lt;&lt;  <span class="string">u8"喵!"</span> ;</span><br><span class="line">        <span class="function">emit <span class="title">miao</span><span class="params">()</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">signals:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">miao</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Jerry.h</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> once</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;QObject&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;QDebug&gt;</span></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Jerry</span> :</span> <span class="keyword">public</span> QObject</span><br><span class="line">&#123;</span><br><span class="line">    Q_OBJECT</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    Jerry(QObject *parent = <span class="literal">nullptr</span>) : QObject(parent)</span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">public</span> slots:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">runAway</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        qDebug() &lt;&lt; <span class="string">u8"那只猫又来了，快溜！"</span> ;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h1 id="信号-槽的使用"><a href="#信号-槽的使用" class="headerlink" title="信号-槽的使用"></a>信号-槽的使用</h1><p>使用比较简单，先说一下使用。</p>
<h2 id="信号的使用"><a href="#信号的使用" class="headerlink" title="信号的使用"></a>信号的使用</h2><p>信号 就是普通的类成员函数，信号只要声明(declare)，不需要实现(implement)，实现由moc(元对象编译器)自动生成。</p>
<p>信号的触发，可以用emit，也可以直接调用函数。</p>
<p>例如:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实例化一个tom对象</span></span><br><span class="line">Tom tom;</span><br><span class="line"><span class="comment">//通过emit发射信号</span></span><br><span class="line">emit tom.miao();</span><br><span class="line"><span class="comment">//直接调用信号。效果和emit一样。</span></span><br><span class="line">tom.miao();</span><br></pre></td></tr></table></figure>

<p>Qt源码的qobejctdefs.h头文件中，可以看到emit宏其实是空的。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//qobejctdefs.h</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> QT_NO_EMIT</span></span><br><span class="line"><span class="meta"># <span class="meta-keyword">define</span> emit</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></table></figure>

<h2 id="槽函数的使用"><a href="#槽函数的使用" class="headerlink" title="槽函数的使用"></a>槽函数的使用</h2><p>槽函数和普通的成员函数一样。。。</p>
<h2 id="信号-槽的”元调用”"><a href="#信号-槽的”元调用”" class="headerlink" title="信号-槽的”元调用”"></a>信号-槽的”元调用”</h2><p>信号-槽特殊的地方，是moc(元对象编译器)为其生成了一份”元信息”,可以通过QMetaObject::invokeMethod的方式调用</p>
<p>例如:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实例化一个tom对象</span></span><br><span class="line">Tom tom;</span><br><span class="line"><span class="comment">//通过invok方式调用信号</span></span><br><span class="line">QMetaObject::invokeMethod(&amp;tom, <span class="string">"miao"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//实例化一个jerry对象</span></span><br><span class="line">Jerry jerry;</span><br><span class="line"><span class="comment">//通过invok方式调用槽</span></span><br><span class="line">QMetaObject::invokeMethod(&amp;jerry, <span class="string">"runAway"</span>);</span><br></pre></td></tr></table></figure>

<p>一般在知道如何声明qobject的场景，没必要多此一举用invoke。</p>
<p>在一些需要”运行期反射”的情况下(头文件都没有,只知道有这么个对象,和函数的名字)，invoke十分有用。</p>
<p>invokeMethod还可以带参数、可以获取返回值，这不是本文的重点，这里就不展开了，详细的可以参考Qt帮助文档和元对象系统。</p>
<h1 id="信号和信号的参数"><a href="#信号和信号的参数" class="headerlink" title="信号和信号的参数"></a>信号和信号的参数</h1><p>信号可以带参数，参数的类型，必须是元对象系统能够识别的类型, 即元类型。（元对象系统后面再细说）</p>
<h2 id="注册元类型"><a href="#注册元类型" class="headerlink" title="注册元类型"></a>注册元类型</h2><p>Qt已经将大部分常用的基础类型，都注册进了元对象系统，可以在QMetaType类中看到。</p>
<p>通常写的继承于QObject的子类，本身已经附带了元信息，可以直接在信号-槽中使用。</p>
<p>不是继承于QObject的结构体、类等自定义类型，可以通过Q_DECLARE_METATYPE宏 和 qRegisterMetaType函数进行注册，之后就可以在信号-槽中使用。</p>
<p>例如：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">MyStruct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">int</span> i;</span><br><span class="line">    ...</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">Q_DECLARE_METATYPE(MyStruct)</span><br></pre></td></tr></table></figure>
<p>或者带命名空间的：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> MyNamespace</span><br><span class="line">&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Q_DECLARE_METATYPE(MyNamespace::MyStruct)</span><br></pre></td></tr></table></figure>

<p>这里说明一下细节，Q_DECLARE_METATYPE宏声明过后，只是生成了元信息，可以被QVariant识别，还不能</p>
<p>用于队列方式的信号、槽，需要用qRegisterMetaType进行注册。而qRegisterMetaType要求”全定义”，也就是</p>
<p>提供类的”复制构造函数”和”赋值操作符”。</p>
<p>前面那种简单类型，C++编译器默认提供浅拷贝的”复制构造函数”和”赋值操作符”实现，可以直接用。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">MyStruct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">int</span> i;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>而复杂一些的类，就要提供”全定义”。</p>
<p>(顺带一提，信号的参数可以是任意注册过的对象，而C++11的lambda、std::bind也是对象，只要注册过，也是可以通过信号参数发送出去的。)</p>
<h1 id="信号-槽的连接-connect函数"><a href="#信号-槽的连接-connect函数" class="headerlink" title="信号-槽的连接 connect函数"></a>信号-槽的连接 connect函数</h1><p>信号与槽，通过connect函数进行连接，之后就可以用信号去触发槽函数了。</p>
<p>连接的一般格式是Connectin = connect(obj1, signal1, obj2, slot1, connectType);</p>
<h2 id="连接的不同写法"><a href="#连接的不同写法" class="headerlink" title="连接的不同写法"></a>连接的不同写法</h2><p>connect函数重载实现了多种不同的参数写法，以Qt5.12为例，大致分为三类:</p>
<p> 元方法式、函数指针式、functor式</p>
<h3 id="元方法式"><a href="#元方法式" class="headerlink" title="元方法式"></a>元方法式</h3><p>元方法式是最常用的写法，函数声明如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">   <span class="comment">//connect(1) 字符串式信号槽</span></span><br><span class="line">   <span class="function"><span class="keyword">static</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="keyword">const</span> QObject *sender, <span class="keyword">const</span> <span class="keyword">char</span> *signal,</span></span></span><br><span class="line"><span class="function"><span class="params">                       <span class="keyword">const</span> QObject *receiver, <span class="keyword">const</span> <span class="keyword">char</span> *member, Qt::ConnectionType = Qt::AutoConnection)</span></span>;</span><br><span class="line">   <span class="comment">//connect(2) QMetaMethod式信号槽</span></span><br><span class="line">   <span class="function"><span class="keyword">static</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="keyword">const</span> QObject *sender, <span class="keyword">const</span> QMetaMethod &amp;signal,</span></span></span><br><span class="line"><span class="function"><span class="params">                       <span class="keyword">const</span> QObject *receiver, <span class="keyword">const</span> QMetaMethod &amp;method,</span></span></span><br><span class="line"><span class="function"><span class="params">                       Qt::ConnectionType type = Qt::AutoConnection)</span></span>;</span><br><span class="line"><span class="comment">//connect(3) 对(1)的重载, 非static去掉receiver</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="keyword">const</span> QObject *sender, <span class="keyword">const</span> <span class="keyword">char</span> *signal,</span></span></span><br><span class="line"><span class="function"><span class="params">                       <span class="keyword">const</span> <span class="keyword">char</span> *member, Qt::ConnectionType type = Qt::AutoConnection)</span> <span class="keyword">const</span></span>;</span><br></pre></td></tr></table></figure>

<p>Qt应用程序中用到最多的是connect(1)的写法，例如:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Tom tom;</span><br><span class="line">Jerry jerry;</span><br><span class="line"><span class="built_in">connect</span>(&amp;tom, SIGNAL(miao()), &amp;jerry, SLOT(runAway()))</span><br></pre></td></tr></table></figure>
<p>其中SIGNAL、SLOT两个宏, 作用是将函数转换成字符串。</p>
<p>connect(1)的实现是靠字符串去查找元方法，以实现连接。</p>
<p>connect(2) 则是把信号槽的字符串换成了元方法QMetaMethod, 一般不会直接用这种写法。</p>
<p>connect(3)是对connect(1)的重载，非静态成员函数，本身有this指针，所以省略了receiver参数。</p>
<h3 id="函数指针式"><a href="#函数指针式" class="headerlink" title="函数指针式"></a>函数指针式</h3><p>函数指针式写法，声明如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//connect(4) 连接信号到qobject的成员函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> QMetaObject::Connection <span class="title">connect</span><span class="params">(<span class="keyword">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal,</span></span></span><br><span class="line"><span class="function"><span class="params">                                    <span class="keyword">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func2&gt;::Object *receiver, Func2 slot,</span></span></span><br><span class="line"><span class="function"><span class="params">                                    Qt::ConnectionType type = Qt::AutoConnection)</span></span>;</span><br><span class="line">								 </span><br><span class="line"><span class="comment">//connect(5) 连接信号到非成员函数。</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> <span class="built_in">std</span>::enable_if&lt;<span class="keyword">int</span>(QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount) &gt;= <span class="number">0</span>, QMetaObject::Connection&gt;::type</span><br><span class="line">           <span class="built_in">connect</span>(<span class="keyword">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, Func2 slot);</span><br><span class="line">   </span><br><span class="line"><span class="comment">//connect(6) 连接信号到非成员函数。比(5)多一个context,可以设置连接类型</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> <span class="built_in">std</span>::enable_if&lt;<span class="keyword">int</span>(QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount) &gt;= <span class="number">0</span> &amp;&amp;</span><br><span class="line">                                         !QtPrivate::FunctionPointer&lt;Func2&gt;::IsPointerToMemberFunction, QMetaObject::Connection&gt;::type</span><br><span class="line">           <span class="built_in">connect</span>(<span class="keyword">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, <span class="keyword">const</span> QObject *context, Func2 slot,</span><br><span class="line">                   Qt::ConnectionType type = Qt::AutoConnection);</span><br></pre></td></tr></table></figure>
<p>connect(4)用的也比较多，用法如下:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Tom tom;</span><br><span class="line">Jerry jerry;</span><br><span class="line"><span class="built_in">connect</span>(&amp;tom, &amp;Tom::miao, &amp;jerry, &amp;Jerry::runAway);</span><br></pre></td></tr></table></figure>
<p>信号-槽换成了C++的 取成员函数指针 的形式。</p>
<p>connect(4)本身的实现，比connect(1)快一些，因为省去了字符串查找的过程。</p>
<p>而连接建立后，从信号触发到槽函数的执行，两种写法是没有区别的。</p>
<p>在一些需要”运行期反射”的情况下(头文件都没有,只知道有这么个对象,和函数的名字),只能用connect(1)。</p>
<p>connect(5)可以连接信号到任意非成员函数指针上。除了槽函数，普通的函数也可以连接。这种连接不支持设置连接类型，可以看作是单纯的函数调用。</p>
<p>connect(6)是对connect(5)的重载,增加了一个context对象代替reveicer对象的作用。这种连接是可以设置连接类型的。</p>
<h4 id="函数重载的处理"><a href="#函数重载的处理" class="headerlink" title="函数重载的处理"></a>函数重载的处理</h4><p>信号-槽函数有重载的情况下，写函数指针式connect会报错，就需要类型转换。</p>
<p>比如：QLocalSocket有一个成员函数error,也有一个信号error,直接写connect会报错的。</p>
<p>Qt为我们提供了QOverload这个模板类，以解决这个问题。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//连接重载过的函数，使用QOverload做leixing 转换</span></span><br><span class="line"><span class="built_in">connect</span>(socket, QOverload&lt;QLocalSocket::LocalSocketError&gt;::of(&amp;QLocalSocket::error), <span class="keyword">this</span>, &amp;XXX::onError);</span><br></pre></td></tr></table></figure>
<p>编译器支持C++14，还可以用qOverload模板函数</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//连接重载过的函数，使用QOverload做leixing 转换</span></span><br><span class="line"><span class="built_in">connect</span>(socket, qOverload&lt;QLocalSocket::LocalSocketError&gt;(&amp;QLocalSocket::error), <span class="keyword">this</span>, &amp;XXX::onError);</span><br></pre></td></tr></table></figure>

<p>还有像QNetworkReply::error、QProcess::finished等等，都有重载，用的时候要转换处理一下。</p>
<h3 id="functor式"><a href="#functor式" class="headerlink" title="functor式"></a>functor式</h3><h4 id="关于functor"><a href="#关于functor" class="headerlink" title="关于functor"></a>关于functor</h4><p>问: 什么是functor？functor有什么用?</p>
<p>答: 在C++11之前, Qt通过自己的实现来推导函数指针及其参数，即QtPrivate::FunctionPointer, 用来处理信号-槽的连接。</p>
<p>C++11带来了lambda, 以及std::bind和std::function, std::function本身可以存储lambda、std::bind以及FunctionPointer。</p>
<p>这时候Qt已有的connect(4)、connect(5)、connect(6)是可以支持FunctionPointer的,而新出现的lambda以及std::bind是不支持的，</p>
<p>QtPrivate::FunctionPointer推导不出这些类型。所以Qt把这些不支持的新类型(主要是lambda和std::bind)称为functor(文档和源码都这么命名)，</p>
<p>并增加了connect(7)和connect(8)以支持functor。</p>
<h4 id="functor"><a href="#functor" class="headerlink" title="functor"></a>functor</h4><p>functor式写法，声明如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//connect(7) 连接信号到任意functor</span></span><br><span class="line">   <span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> <span class="built_in">std</span>::enable_if&lt;QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount == <span class="number">-1</span>, QMetaObject::Connection&gt;::type</span><br><span class="line">           <span class="built_in">connect</span>(<span class="keyword">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, Func2 slot);</span><br><span class="line">		</span><br><span class="line">   <span class="comment">//connect(8) 连接信号到任意functor。比(7)多一个context,可以设置连接类型</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> Func1, <span class="keyword">typename</span> Func2&gt;</span><br><span class="line">   <span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">typename</span> <span class="built_in">std</span>::enable_if&lt;QtPrivate::FunctionPointer&lt;Func2&gt;::ArgumentCount == <span class="number">-1</span>, QMetaObject::Connection&gt;::type</span><br><span class="line">           <span class="built_in">connect</span>(<span class="keyword">const</span> <span class="keyword">typename</span> QtPrivate::FunctionPointer&lt;Func1&gt;::Object *sender, Func1 signal, <span class="keyword">const</span> QObject *context, Func2 slot,</span><br><span class="line">                   Qt::ConnectionType type = Qt::AutoConnection);</span><br></pre></td></tr></table></figure>

<p>connect(7)可以连接信号到任意lambda、std::bind上。</p>
<p>connect(8)是对(7)的重载，增加了一个context对象代替reveicer对象的作用。这种连接是可以设置连接类型的。</p>
<h2 id="connect的连接类型"><a href="#connect的连接类型" class="headerlink" title="connect的连接类型"></a>connect的连接类型</h2><p>connectType为连接类型，默认为AutoConnection，即Qt自动处理，大部分情况下也不用管。个别情况，需要手动指定。</p>
<p>可选的连接类型有<br>自动 AutoConnection<br>直连 DirectConnection<br>队列 QueuedConnection<br>唯一连接 UniqueConnection</p>
<p>自动处理的逻辑是，如果发送信号的线程和receiver在同一个线程，就是DirectConnection(直接函数调用),不是同一个线程，则转换为QueuedConnection。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">&#x2F;&#x2F;引用自《Qt原理-窥探信号槽的实现细节》</span><br><span class="line"></span><br><span class="line">如果信号-槽连接方式为QueuedConnection，不论是否在同一个线程，按队列处理。</span><br><span class="line"></span><br><span class="line">如果信号-槽连接方式为Auto，且不在同一个线程，也按队列处理。</span><br><span class="line"></span><br><span class="line">如果信号-槽连接方式为阻塞队列BlockingQueuedConnection，按阻塞处理。</span><br><span class="line"> </span><br><span class="line">(注意同一个线程就不要按阻塞队列调用了。因为同一个线程，同时只能做一件事，</span><br><span class="line"></span><br><span class="line">本身就是阻塞的，直接调用就好了，如果走阻塞队列，则多了加锁的过程。如果槽中又发了</span><br><span class="line"></span><br><span class="line">同样的信号，就会出现死锁：加锁之后还未解锁，又来申请加锁。)</span><br><span class="line"></span><br><span class="line">队列处理，就是把槽函数的调用，转化成了QMetaCallEvent事件，通过QCoreApplication::postEvent</span><br><span class="line"></span><br><span class="line">放进了事件循环, 等到下一次事件分发，相应的线程才会去调用槽函数。</span><br></pre></td></tr></table></figure>

<p>下面举例一些需要手动指定连接类型的场景：</p>
<p>例1-跨多个线程：</p>
<p>A线程中写connect，让B线程中的信号连到C线程的槽中，希望C的槽在C中执行。</p>
<p>这种情况要明确指定QueuedConnection，不写的话按照Auto处理，C中的槽会在A中执行。</p>
<p>例2-跨线程DirectConnection</p>
<p>(这种用法在Qml的渲染引擎SceneGraph中比较常见)。</p>
<p>A线程为内部代码，不能修改，一些特定的节点会有信号发出。</p>
<p>B线程为用户代码，有一些功能函数，希望在A线程中去执行。</p>
<p>这种情况，将A的信号连接到B的函数，连接方式指定为DirectConnection，就可以把B的函数插入到A线程发信号的地方了。</p>
<p>效果类似于子类重载父类的函数。</p>
<h2 id="connect的返回值"><a href="#connect的返回值" class="headerlink" title="connect的返回值"></a>connect的返回值</h2><p>connect的返回值为QMetaObject::Connection,代表一个连接。大部分情况下，不用管返回值。</p>
<p>Connection可以用来验证链接是否有效，可以用来断开连接。</p>
<p>一般用disconnect函数就可以断开连接；而signal-functor的这种形式的连接，没有object的存在，只能用Connection断开。</p>

    </div>
    
    <div class="reward" ontouchstart>
    <div class="reward-wrap">赏
        <div class="reward-box">
            
            <span class="reward-type">
                <img class="alipay" src="/img/zhifubao.jpg"><b>支付宝打赏</b>
            </span>
            
            
            <span class="reward-type">
                <img class="wechat" src="/img/weixin.jpg"><b>微信打赏</b>
            </span>
            
        </div>
    </div>
    <p class="reward-tip">
        很有收获,请作者喝杯茶
    </p>
</div>
    
    <div class="post-footer">
        <div>
            
            转载声明：
            文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a>进行许可, 转载请注明出处 © <a href="https://jaredtao.github.io" target="_blank">武威的涛哥</a>
            
            
        </div>
        <div>
            
        </div>
    </div>
</article>
<div class="article-nav prev-next-wrap clearfix">
    
    <a href="/2019/09/12/Qt/%E7%8E%A9%E8%BD%ACQt(9)-%E7%A8%8B%E5%BA%8F%E5%8F%91%E5%B8%83%E6%8C%87%E5%8D%97/" class="pre-post btn btn-default" title='玩转Qt(9)-程序发布指南'>
        <i class="fa fa-angle-left fa-fw"></i><span class="hidden-lg">上一篇</span>
        <span class="hidden-xs">
            玩转Qt(9)-程序发布指南</span>
    </a>
    
    
    <a href="/2019/08/30/Qt/%E7%8E%A9%E8%BD%ACQt(7)-%E7%AA%A5%E6%8E%A2%E4%BF%A1%E5%8F%B7%E6%A7%BD%E7%9A%84%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82/" class="next-post btn btn-default" title='玩转Qt(7)-窥探信号槽的实现细节'>
        <span class="hidden-lg">下一篇</span>
        <span class="hidden-xs">
            玩转Qt(7)-窥探信号槽的实现细节</span><i class="fa fa-angle-right fa-fw"></i>
    </a>
    
</div>

<div id="comments">
    

<div id="vcomments" class="valine"></div>

<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="/assets/valine.min.js"></script>

<script>
new Valine({
    av: AV,
    el: '#vcomments',
    appId: 'qegD4552u6FU9HoNDvRsonko-gzGzoHsz',
    appKey: 'eitB2FV5n2qyMHcDM7x6EQpt',
    placeholder: '说点什么吧',
    notify: false,
    verify: true,
    avatar: 'mm',
    meta: 'nick,mail'.split(','),
    pageSize: '10',
    path: window.location.pathname,
    lang: 'zh-CN'.toLowerCase()
})
</script>


</div>


                </main>
                
                    <aside id="article-toc" role="navigation" class="col-md-4">
    <div class="widget">
        <h3 class="title">
            文章目录
        </h3>
        
        <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#简介"><span class="toc-text">简介</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#信号与槽的声明"><span class="toc-text">信号与槽的声明</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#信号-槽的使用"><span class="toc-text">信号-槽的使用</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#信号的使用"><span class="toc-text">信号的使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#槽函数的使用"><span class="toc-text">槽函数的使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#信号-槽的”元调用”"><span class="toc-text">信号-槽的”元调用”</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#信号和信号的参数"><span class="toc-text">信号和信号的参数</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#注册元类型"><span class="toc-text">注册元类型</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#信号-槽的连接-connect函数"><span class="toc-text">信号-槽的连接 connect函数</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#连接的不同写法"><span class="toc-text">连接的不同写法</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#元方法式"><span class="toc-text">元方法式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#函数指针式"><span class="toc-text">函数指针式</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#函数重载的处理"><span class="toc-text">函数重载的处理</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#functor式"><span class="toc-text">functor式</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#关于functor"><span class="toc-text">关于functor</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#functor"><span class="toc-text">functor</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#connect的连接类型"><span class="toc-text">connect的连接类型</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#connect的返回值"><span class="toc-text">connect的返回值</span></a></li></ol></li></ol>
        
    </div>
</aside>
                
            </div>
        </div>
    </section>
    <footer class="main-footer">
    <div class="container">
        <div class="row">
        </div>
    </div>
</footer>
<a id="back-to-top" class="icon-btn hide">
    <i class="fa fa-chevron-up"></i>
</a>
    <div class="copyright">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="busuanzi">
    
    访问量:
    <strong id="busuanzi_value_site_pv">
        <i class="fa fa-spinner fa-spin"></i>
    </strong>
    &nbsp; | &nbsp;
    访客数:
    <strong id="busuanzi_value_site_uv">
        <i class="fa fa-spinner fa-spin"></i>
    </strong>
    
</div>
            </div>
            <div class="col-sm-12">
                <span>Copyright &copy;
                    2019
                    
                </span> |
                <span>
                    Powered by <a href="//hexo.io" class="copyright-links" target="_blank" rel="nofollow">Hexo</a>
                </span> |
                <span>
                    Theme by <a href="//github.com/shenliyang/hexo-theme-snippet.git" class="copyright-links" target="_blank" rel="nofollow">Snippet</a>
                </span>
            </div>
        </div>
    </div>
</div>



<script src="/assets/tagcanvas.min.js?rev=2.9.js"></script>

<script>
var tagOption = {
    textColour: '#444', // 字体颜色
    outlineMethod: 'block', // 选中模式
    outlineColour: '#FFDAB9', // 选中模式的颜色
    interval: 30 || 30, // 动画帧之间的时间间隔，值越大，转动幅度越大
    textHeight: 13,
    outlineRadius: 3,
    freezeActive: true || '', // 选中的标签是否继续滚动
    frontSelect: true || '', // 不选标签云后部的标签
    initial: [0.1, -0.1],
    depth: 0.5,
    decel: 0.95,
    maxSpeed: 0.03,
    reverse: true || '', // 是否反向触发
    fadeIn: 500, // 进入动画时间
    wheelZoom: false || '' // 是否启用鼠标滚轮
}
TagCanvas.Start('tag-cloud-3d', '', tagOption);
</script>


<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>


<script src="/js/app.js?rev=@@hash.js"></script>

</body>
</html>